﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using FasterWPF.Selectors;

namespace FasterWPF
{
    public static class BrushExt
    {
        /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Headered Content to Perform Change Skin Action on
        /// </summary>
        /// <param name="startingPoint"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        public static void ChangeSkinOnHeaderedContent(Window startingPoint, Brush newBrush, Action<object, Brush> changeSkinAction)
        {
            List<object> elementsFromHeaderedContentControls = FasterWPF.Selectors.Select.SelectAllHeaderedContent(startingPoint, false);

            elementsFromHeaderedContentControls.ForEach(obj => { changeSkinAction(obj, newBrush); });
        }

        /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Elements by Type to Perform Change Skin Action on
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="startingPoint"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        public static void ChangeSkin<T>(Window startingPoint, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction)
            where T: FrameworkElement
        {
            List<T> elementsOfTypeT = Selectors.Select.SelectFrameworkElementsByType<T>(startingPoint);

            elementsOfTypeT.ForEach(t => { changeSkinAction(t, newBrush); });
        }

        /// <summary>
        ///  Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Elements by Type to Perform Change Skin Action on, Including Based On a Predicate
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="startingPoint"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        /// <param name="includeInChangeSkin"></param>
        public static void ChangeSkin<T>(Window startingPoint, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction, Predicate<T> includeInChangeSkin)
    where T : FrameworkElement
        {
            List<T> elementsOfTypeT = Selectors.Select.SelectFrameworkElementsByType<T>(startingPoint);

            elementsOfTypeT.ForEach(t => { if (includeInChangeSkin(t)) { changeSkinAction(t, newBrush); } });
        }

       /// <summary>
       /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Elements by Type to Perform Change Skin Actions on
       /// </summary>
       /// <typeparam name="T"></typeparam>
       /// <typeparam name="K"></typeparam>
       /// <param name="startingPoint"></param>
       /// <param name="newBrush"></param>
       /// <param name="changeSkinAction"></param>
       /// <param name="newBrush2"></param>
       /// <param name="changeSkinAction2"></param>
        public static void ChangeSkin<T, K>(Window startingPoint, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction, Brush newBrush2, Action<FrameworkElement, Brush> changeSkinAction2)
        where T : FrameworkElement
        where K : FrameworkElement
        {
            FrameworkElement w = startingPoint as FrameworkElement;

            List<T> elementsOfTypeT = Selectors.Select.SelectFrameworkElementsByType<T>(w);

            elementsOfTypeT.ForEach(t => { changeSkinAction(t, newBrush); });

            List<K> elementsOfTypeT2 = Selectors.Select.SelectFrameworkElementsByType<K>(w);

            elementsOfTypeT2.ForEach(k => { changeSkinAction2(k, newBrush2); });
        }
        
        /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Elements by Type to Perform Change Skin Actions on
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <typeparam name="L"></typeparam>
        /// <param name="startingPoint"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        /// <param name="newBrush2"></param>
        /// <param name="changeSkinAction2"></param>
        /// <param name="newBrush3"></param>
        /// <param name="changeSkinAction3"></param>
        public static void ChangeSkin<T, K, L>(Window startingPoint, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction, Brush newBrush2, Action<FrameworkElement, Brush> changeSkinAction2, Brush newBrush3, Action<FrameworkElement, Brush> changeSkinAction3)
            where T : FrameworkElement
            where K : FrameworkElement
            where L : FrameworkElement
        {
            //start from the top . . .
            FrameworkElement w = startingPoint as FrameworkElement;

            List<T> elementsOfTypeT = Selectors.Select.SelectFrameworkElementsByType<T>(w);

            elementsOfTypeT.ForEach(t => { changeSkinAction(t, newBrush); });

            List<K> elementsOfTypeT2 = Selectors.Select.SelectFrameworkElementsByType<K>(w);

            elementsOfTypeT2.ForEach(k => { changeSkinAction2(k, newBrush2); });

            List<L> elementsOfTypeT3 = Selectors.Select.SelectFrameworkElementsByType<L>(w);

            elementsOfTypeT3.ForEach(l => { changeSkinAction3(l, newBrush3); });
        }

        /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Elements by Type to Perform Change Skin Actions on, Including Based On Predicates
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <param name="startingPoint"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        /// <param name="includeInChangeSkin"></param>
        /// <param name="newBrush2"></param>
        /// <param name="changeSkinAction2"></param>
        /// <param name="includeInChangeSkin2"></param>
        public static void ChangeSkin<T, K>(Window startingPoint, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction, Predicate<T> includeInChangeSkin, Brush newBrush2, Action<FrameworkElement, Brush> changeSkinAction2, Predicate<K> includeInChangeSkin2)
        where T : FrameworkElement
        where K : FrameworkElement
        {
            List<T> elementsOfTypeT = Selectors.Select.SelectFrameworkElementsByType<T>(startingPoint);

            elementsOfTypeT.ForEach(t => { if (includeInChangeSkin(t)) { changeSkinAction(t, newBrush); } });

            List<K> elementsOfTypeT2 = Selectors.Select.SelectFrameworkElementsByType<K>(startingPoint);

            elementsOfTypeT2.ForEach(k => { if (includeInChangeSkin2(k)) { changeSkinAction2(k, newBrush2); } });
        }

        /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Containers by Type to Perform Change Skin Actions on
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="startingPoint"></param>
        /// <param name="c"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        public static void ChangeSkinOnContainers<T>(Window startingPoint, ContainerType c, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction)
    where T : FrameworkElement
        {
            List<T> elementsOfTypeTComposites = Selectors.Select.SelectContainersFromAllTComposites<T>(startingPoint, c, false);

            elementsOfTypeTComposites.ForEach(t => { changeSkinAction(t, newBrush); });
        }

       /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Containers by Type to Perform Change Skin Actions on, Including Based On a Predicate
       /// </summary>
       /// <typeparam name="T"></typeparam>
       /// <param name="startingPoint"></param>
       /// <param name="c"></param>
       /// <param name="newBrush"></param>
       /// <param name="changeSkinAction"></param>
       /// <param name="includeInChangeSkin"></param>
        public static void ChangeSkinOnContainers<T>(Window startingPoint, ContainerType c, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction, Predicate<T> includeInChangeSkin)
            where T: FrameworkElement
        {
            List<T> elementsOfTypeTComposites = Selectors.Select.SelectContainersFromAllTComposites<T>(startingPoint, c, false);

            elementsOfTypeTComposites.ForEach(t => { if (includeInChangeSkin(t)) { changeSkinAction(t, newBrush); } });
        }
        
        /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Elements by Class to Perform Change Skin Action on
        /// </summary>
        /// <param name="startingPoint"></param>
        /// <param name="selectorClassName"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        public static void ChangeSkinByClass(Window startingPoint, string selectorClassName, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction)
        {
            List<FrameworkElement> elementsWithMatchingClass = Selectors.Select.SelectAllFrameworkElementsByClass(startingPoint, selectorClassName, false);

            elementsWithMatchingClass.ForEach(fe => { changeSkinAction(fe, newBrush); });
        }

        /// <summary>
        /// Pass In a Window (e.g App.Current.MainWindow) as the Starting Point before then Recursing Down to Collect Elements by Class to Perform Change Skin Action on, Including Based On a Predicate
        /// </summary>
        /// <param name="startingPoint"></param>
        /// <param name="selectorClassName"></param>
        /// <param name="newBrush"></param>
        /// <param name="changeSkinAction"></param>
        /// <param name="includeInChangeSkin"></param>
        public static void ChangeSkinByClass(Window startingPoint, string selectorClassName, Brush newBrush, Action<FrameworkElement, Brush> changeSkinAction, Predicate<FrameworkElement> includeInChangeSkin)
        {
            List<FrameworkElement> elementsWithMatchingClass = Selectors.Select.SelectAllFrameworkElementsByClass(startingPoint, selectorClassName, false);

            elementsWithMatchingClass.ForEach(fe => { if (includeInChangeSkin(fe)) { changeSkinAction(fe, newBrush); } });
        }

        public static string CreateColorStringFromRGB(byte red, byte green, byte blue, byte alpha)
        {
            Brush b = CreateSolidColorBrushFromRGB(red, green, blue, alpha);
            var converter = new System.Windows.Media.BrushConverter();
            var brush = converter.ConvertToString(b);
            return brush;
        }

        public static Brush CreateImageBrush(string imageUri, UriKind uriKind, double imageWidth)
        {
            ImageBrush b = new ImageBrush();
            
            //Image finalImage = new Image();
            //finalImage.Width = imageWidth;

            var uriSource = new Uri(imageUri, uriKind);
            var myImage = new BitmapImage(uriSource);
            myImage.Freeze();
            //finalImage.Source = myImage;

            b.ImageSource = myImage;

            return b;
        }

        public static LinearGradientBrush CreateGradientBrush(byte gradientStop1red, byte gradientStop1green, byte gradientStop1blue, byte gradientStop1alpha, double gradientStop1Offset, byte gradientStop2red, byte gradientStop2green, byte gradientStop2blue, byte gradientStop2alpha, double gradientStop2Offset, double startPointX, double startPointY, double endPointX, double endPointY)
        {
            GradientStopCollection gradientStopsLinearBrush = new GradientStopCollection();
            gradientStopsLinearBrush.Add(
                new GradientStop(Color.FromArgb(gradientStop1alpha, gradientStop1red, gradientStop1green, gradientStop1blue), gradientStop1Offset));
            gradientStopsLinearBrush.Add(
                new GradientStop(Color.FromArgb(gradientStop2alpha, gradientStop2red, gradientStop2green, gradientStop2blue), gradientStop2Offset));

            LinearGradientBrush backgroundLinearBrush = new LinearGradientBrush(gradientStopsLinearBrush)
            {
                StartPoint = new Point(startPointX, startPointY),
                EndPoint = new Point(endPointX, endPointY)
            };
            return backgroundLinearBrush;
        }

        public static LinearGradientBrush CreateGradientBrush(byte gradientStop1red, byte gradientStop1green, byte gradientStop1blue, byte gradientStop1alpha, double gradientStop1Offset, string gradientStop2Color, double gradientStop2Offset, double startPointX, double startPointY, double endPointX, double endPointY)
        {
            GradientStopCollection gradientStopsLinearBrush = new GradientStopCollection();
            gradientStopsLinearBrush.Add(
                new GradientStop(Color.FromArgb(gradientStop1alpha, gradientStop1red, gradientStop1green, gradientStop1blue), gradientStop1Offset));
            gradientStopsLinearBrush.Add(
                new GradientStop((Color)ColorConverter.ConvertFromString(gradientStop2Color), gradientStop2Offset));

            LinearGradientBrush backgroundLinearBrush = new LinearGradientBrush(gradientStopsLinearBrush)
            {
                StartPoint = new Point(startPointX, startPointY),
                EndPoint = new Point(endPointX, endPointY)
            };
            return backgroundLinearBrush;
        }

        public static LinearGradientBrush CreateGradientBrush(string gradientStop1Color, double gradientStop1Offset, byte gradientStop2red, byte gradientStop2green, byte gradientStop2blue, byte gradientStop2alpha, double gradientStop2Offset, double startPointX, double startPointY, double endPointX, double endPointY)
        {
            GradientStopCollection gradientStopsLinearBrush = new GradientStopCollection();
            gradientStopsLinearBrush.Add(
                new GradientStop((Color)ColorConverter.ConvertFromString(gradientStop1Color), gradientStop1Offset));
            gradientStopsLinearBrush.Add(
                new GradientStop(Color.FromArgb(gradientStop2alpha, gradientStop2red, gradientStop2green, gradientStop2blue), gradientStop2Offset));

            LinearGradientBrush backgroundLinearBrush = new LinearGradientBrush(gradientStopsLinearBrush)
            {
                StartPoint = new Point(startPointX, startPointY),
                EndPoint = new Point(endPointX, endPointY)
            };
            return backgroundLinearBrush;
        }

        public static LinearGradientBrush CreateGradientBrush(string gradientStop1Color, double gradientStop1Offset, string gradientStop2Color, double gradientStop2Offset, double startPointX, double startPointY, double endPointX, double endPointY)
        {
            GradientStopCollection gradientStopsLinearBrush = new GradientStopCollection();
            gradientStopsLinearBrush.Add(
                new GradientStop((Color)ColorConverter.ConvertFromString(gradientStop1Color), gradientStop1Offset));
            gradientStopsLinearBrush.Add(
                new GradientStop((Color)ColorConverter.ConvertFromString(gradientStop2Color), gradientStop2Offset));

            LinearGradientBrush backgroundLinearBrush = new LinearGradientBrush(gradientStopsLinearBrush)
            {
                StartPoint = new Point(startPointX, startPointY),
                EndPoint = new Point(endPointX, endPointY)
            };
            return backgroundLinearBrush;
        }

       //public static string CreateGradientBrushStr(string gradientStop1Color, string gradientStop2Color,  string gradientStop3Color, string gradientStop4Color)
       // {
       //     string xaml = "<LinearGradientBrush StartPoint=\"0,0\" EndPoint=\"0,1\"><GradientStop Offset=\"0\" Color=" + gradientStop1Color + "/><GradientStop Offset=\"0.49\" Color=" + gradientStop2Color + "/><GradientStop Offset=\"0.50\" Color=" + gradientStop3Color + " /><GradientStop Offset=\"1\" Color=" + gradientStop4Color + " /></LinearGradientBrush>";

       //     return xaml;
       // }

        public static RadialGradientBrush CreateRadialGradientBrush(string gradientStop1Color, double gradientStop1Offset, byte gradientStop2red, byte gradientStop2green, byte gradientStop2blue, byte gradientStop2alpha, double gradientStop2Offset, double originPointX, double originPointY, double centerPointX, double centerPointY)
        {
            GradientStopCollection gradientStopsLinearBrush = new GradientStopCollection();
            gradientStopsLinearBrush.Add(
                new GradientStop((Color)ColorConverter.ConvertFromString(gradientStop1Color), gradientStop1Offset));
            gradientStopsLinearBrush.Add(
                new GradientStop(Color.FromArgb(gradientStop2alpha, gradientStop2red, gradientStop2green, gradientStop2blue), gradientStop2Offset));

            RadialGradientBrush backgroundLinearBrush = new RadialGradientBrush(gradientStopsLinearBrush)
            {
                GradientOrigin = new Point(originPointX, originPointY),
                Center = new Point(centerPointX, centerPointY)
            };
            return backgroundLinearBrush;
        }


        public static Brush CreateSolidColorBrushFromString(string color)
        {
            Brush newBrush = (Brush)(new System.Windows.Media.SolidColorBrush((Color)ColorConverter.ConvertFromString(color)));
            return newBrush;
        }

        public static Brush CreateSolidColorBrushFromRGB(byte red, byte green, byte blue, byte alpha)
        {
            Brush newBrush = (Brush)(new System.Windows.Media.SolidColorBrush(new
System.Windows.Media.Color()
            {
                R = red,
                G = green,
                B = blue,
                A = alpha
            }));
            return newBrush;
        }

        public static Color CreateColorFromString(string color)
        {
            TypeConverter tc2 = new ColorConverter();
            Color newColor = (Color)tc2.ConvertFromString(color);
            return newColor;
        }

        public static Brush CreateGeometryBrush(Brush background, Pen pen, Geometry geometry)
        {
            GeometryDrawing gtd = new GeometryDrawing();
            gtd.Brush = background;
            gtd.Pen = pen;
            gtd.Geometry = geometry;
            
            DrawingBrush newBrush = new DrawingBrush();
            newBrush.Drawing = gtd;

            return newBrush;
        }
    }
}
